import java.io.*;
import java.util.*;
import com.aliasi.classify.*; 
import com.aliasi.chunk.Chunk;
import com.aliasi.chunk.Chunker;
import com.aliasi.chunk.Chunking;
import com.aliasi.dict.DictionaryEntry;
import com.aliasi.dict.MapDictionary;
import com.aliasi.dict.TrieDictionary;
import com.aliasi.dict.Dictionary;
import com.aliasi.dict.ExactDictionaryChunker;
import com.aliasi.tokenizer.IndoEuropeanTokenizerFactory;
import edu.jhu.htm.core.*;

public class TermModelClassifier implements ConditionalClassifier<CharSequence> {

  HTMindexImp spindex;
  MapDictionary<String> dictionary;
  Set<String> classes;
  ExactDictionaryChunker chunker;
  boolean trained;

  public TermModelClassifier ( int resolution ) { 
	spindex = new HTMindexImp(resolution);
	dictionary = new MapDictionary<String>();
	classes = new HashSet<String>();
	trained = false;
  }
    
  private String getTrixel ( Double lat , Double lon )  {
        if ( lat == 0.0 ) lat = 0.25;
        if ( lat == -90.0 ) lat = -89.75;
        if ( lat == 90.0 ) lat = 89.75;
        if ( lon == 0.0 ) lon = 0.25;
        if ( lon == -180.0 ) lon = -179.75;
        if ( lon == 180.0 ) lon = 179.75;
        try { 
            String value = spindex.lookup( lon , lat );
            return value;
        } catch ( Exception ex ) { return null; }
  }

  public void train( String file ) throws IOException {
	  BufferedReader input = new BufferedReader(new FileReader(file));
	  String line = null;
	  while ( (line = input.readLine()) != null ) try {
		String fields[] = line.split("\t");
		String trixel = getTrixel(new Double(fields[4]),new Double(fields[5]));
		classes.add(trixel);
		dictionary.addEntry(new DictionaryEntry<String>(fields[1].trim(),trixel,1.0));
	        if (fields[3].trim().length() > 0 ) for ( String altName : fields[3].split(",") ) {
			dictionary.addEntry(new DictionaryEntry<String>(altName.trim(),trixel,1.0));
		}
	  } catch ( Exception ex ) { }
	  input.close();
	  chunker = new ExactDictionaryChunker(dictionary,IndoEuropeanTokenizerFactory.INSTANCE,true,false);
	  trained = true;
  }

  public void compileTo(ObjectOutput objOut) throws IOException {
	objOut.writeInt(spindex.maxlevel_);
	objOut.writeInt(classes.size());
	for ( String c : classes ) objOut.writeUTF(c);
	objOut.writeObject(dictionary);
  }

  public static TermModelClassifier read ( ObjectInput objIn ) throws IOException, ClassNotFoundException {
	int resolution = objIn.readInt();
	int size = objIn.readInt();
	TermModelClassifier aux = new TermModelClassifier(resolution);
	for ( int i = 0; i< size; i++) aux.classes.add(objIn.readUTF());
	MapDictionary<String> dictionary = (MapDictionary<String>)(objIn.readObject());
	aux.chunker = new ExactDictionaryChunker(dictionary,IndoEuropeanTokenizerFactory.INSTANCE,true,false);
	aux.trained = true;
	return aux;
  }

  public ConditionalClassification classify(CharSequence input) throws IllegalArgumentException {
	if ( trained == false ) throw new IllegalArgumentException("Classifier is not yet trained");
	Map<String,Double> scores = new HashMap<String,Double>();
	for ( String c : classes ) scores.put(c,0.0);
        Chunking chunking = chunker.chunk(input);
	Double total = 0.0;
        for ( Chunk chunk : chunking.chunkSet() ) {
            String type = chunk.type();
            double score = chunk.score();
  	    scores.put(type, scores.get(type) + score);
	    total += score;
        }
	for ( String type : scores.keySet() ) scores.put(type , (scores.get(type) + 1.0) / ( total + scores.keySet().size()));
	String[] categories = new String[scores.keySet().size()];
	double[] conditionalProbs = new double[scores.keySet().size()];
	int i = 0;	
	for ( String type : scores.keySet() ) {
		categories[i] = type;
		conditionalProbs[i] = scores.get(type);
		i++;
	}
	return new ConditionalClassification(categories,conditionalProbs);
  } 

  public static void main ( String args[] ) throws Exception {
	TermModelClassifier c = new TermModelClassifier(6);
	c.train(args[0]);
	ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(args[1]));
	c.compileTo(out);
	out.close();
	c = read(new ObjectInputStream(new FileInputStream(args[1])));
	BufferedReader input = new BufferedReader(new FileReader(args[2]));
	String line = null;
	int total = 0;
	int correct = 0;
	while ( (line = input.readLine()) != null ) {
		String fields[] = line.split("\t");
		ConditionalClassification result = c.classify(fields[5]);
		String best = result.bestCategory();
		String real = c.getTrixel(new Double(fields[3]),new Double(fields[4]));
		if ( best.equals(real) ) correct++;
		total++;
	}
	System.out.println("Accuracy : " + (((double)correct) / ((double)total)) );
  }

}
